<template>
  <div class="relative" @mouseenter="onMouseenter" @mouseleave="onMouseleave">
    <div>
      <div ref="referenceTarget">
        <!-- 具名插槽: 触发弹层的视图 -->
        <slot name="reference"></slot>
      </div>
    </div>
    <!-- 气泡展示 -->
    <transition name="slide">
      <div
        ref="contentTarget"
        v-if="isViable"
        class="absolute p-1 z-20 bg-white border"
        :style="contentStyle"
      >
        <!-- 匿名插槽 弹出层视图展示的内容 -->
        <slot></slot>
      </div>
    </transition>
  </div>
</template>
<script>
// 左上
const PROP_TOP_LEFT = 'top-left'
// 左下
const PROP_TOP_RIGHT = 'top-right'
// 右上
const PROP_BOTTOM_LEFT = 'bottom-left'
// 右下
const PROP_BOTTOM_RIGHT = 'bottom-right'

const placementEunm = [
  PROP_TOP_LEFT,
  PROP_TOP_RIGHT,
  PROP_BOTTOM_LEFT,
  PROP_BOTTOM_RIGHT
]
</script>

<script setup>
import { nextTick, ref, watch } from 'vue-demi'

// 指定所有可选位置的常量 并生成enum
// 通过prop控制常量
// 获取元素的dom 创建读取元素尺寸的方法
// 生成气泡的样式对象 用来控制每个位置对应样式
// 根据prop 并计算样式对象

const props = defineProps({
  // 控制气泡弹出位置，并给出开发者错误提示
  plcaement: {
    type: String,
    default: PROP_TOP_LEFT,
    validator(val) {
      const result = placementEunm.includes(val)
      if (!result) {
        throw new Error(
          `你的 placement 必须是${placementEunm.join('、')}中的一个`
        )
      }
      return result
    }
  }
})
const isViable = ref(false)

// 移入
const onMouseenter = () => {
  isViable.value = true
}

// 移出
const onMouseleave = () => {
  isViable.value = false
}

// 计算元素尺寸
const referenceTarget = ref(null)
const contentTarget = ref(null)
const useElmentgetSize = (target) => {
  if (!target) return
  return {
    width: target.offsetWidth,
    height: target.offsetHeight
  }
}

// 气泡样式
const contentStyle = ref({
  top: 0,
  left: 0
})

// 计算 期望 气泡展示的时候再次计算
watch(isViable, (val) => {
  if (!val) return
  // 等待元素渲染
  // vue 在数据改变之后，需要等待一段时间 dom 才会改变 可以通过nexttick回调函数来监听参数

  nextTick(() => {
    switch (props.plcaement) {
      // 左上
      case PROP_TOP_LEFT:
        contentStyle.value.top = 0
        contentStyle.value.left =
          -useElmentgetSize(contentTarget.value).width + 'px'
        break
      // 右上
      case PROP_TOP_RIGHT:
        contentStyle.value.top = 0
        contentStyle.value.left =
          useElmentgetSize(referenceTarget.value).width + 'px'
        break
      // 左下
      case PROP_BOTTOM_LEFT:
        contentStyle.value.top =
          useElmentgetSize(referenceTarget.value).height + 'px'
        contentStyle.value.left =
          -useElmentgetSize(contentTarget.value).width + 'px'
        break
      // 右下
      case PROP_BOTTOM_RIGHT:
        contentStyle.value.top =
          useElmentgetSize(referenceTarget.value).height + 'px'
        contentStyle.value.left =
          useElmentgetSize(referenceTarget.value).width + 'px'
        break
    }
  })
})
</script>

<style lang="scss" scoped>
.slide-enter-active,
.slide-leave-active {
  transition: opacity 0.3s, transform 0.3s;
}

.slide-enter-from,
.slide-leave-to {
  opacity: 0;
  transform: translateY(20px);
}
</style>
